package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"sync/atomic"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/widget"
)

const configFile = "config.json"

type AppConfig struct {
	LastPath  string `json:"lastPath"`
	InputSear string `json:"inputSear"` // 暂时没用
	InputLua  string `json:"inputLua"`
	InputJson string `json:"inputJson"`
}

type AppData struct {
	selectedFiles map[string]bool
	config        AppConfig       //文件路径
	leftContent   *fyne.Container //左边可选择框
	filtration    string          //搜索词
}

// 自定义固定尺寸布局
type fixedLayout struct {
	width, height float32
}

// // 创建固定尺寸的空白组件
func createSpacer(width, height float32) fyne.CanvasObject {
	return container.New(
		&fixedLayout{width: width, height: height},
	)
}
func (f *fixedLayout) Layout(_ []fyne.CanvasObject, _ fyne.Size) {}
func (f *fixedLayout) MinSize(_ []fyne.CanvasObject) fyne.Size {
	return fyne.NewSize(f.width, f.height)
}

func main() {
	myApp := app.New()
	myWindow := myApp.NewWindow("XLSX 文件转换器")
	myWindow.Resize(fyne.NewSize(800, 600))

	appData := &AppData{
		selectedFiles: make(map[string]bool),
		config:        loadConfig(),
	}

	// 左侧区域
	searchEntry := widget.NewEntry()
	searchEntry.SetPlaceHolder("搜索文件...")
	searchEntry.OnChanged = func(s string) {
		// 实现搜索过滤逻辑
		appData.filtration = s
		loadFiles(appData.config.LastPath, appData.leftContent, appData, appData.filtration,false)
	}

	appData.leftContent = container.NewVBox()
	fileList := container.NewVScroll(appData.leftContent)
	leftPanel := container.NewBorder(searchEntry, nil, nil, nil, fileList)

	// 路径选择组件
	pathInput := widget.NewEntry()
	pathInput.SetPlaceHolder("选择或输入XLSX文件目录")
	pathInput.SetText(appData.config.LastPath)
	// 在 pathInput 定义后添加 OnChanged 回调
	pathInput.OnChanged = func(s string) {
		appData.config.LastPath = s // 实时更新配置中的路径
	}

	selectButton := widget.NewButton("浏览...", func() {
		dialog.NewFolderOpen(func(dir fyne.ListableURI, err error) {
			if err == nil && dir != nil {
				path := dir.String()[7:] // 移除 "file://" 前缀
				pathInput.SetText(path)
				appData.config.LastPath = path
				loadFiles(path, appData.leftContent, appData, "",false)
			}
		}, myWindow).Show()
	})

	// 自动加载初始路径
	if appData.config.LastPath != "" {
		loadFiles(appData.config.LastPath, appData.leftContent, appData, "",false)
	}

	// 右侧区域 (40%)
	windowWidth := float32(800)
	windowHeight := float32(600)

	// 计算百分比间距
	rightWidth := windowWidth * 0.4
	inputSpacer := windowHeight * 0.02    // 1%
	btnBetweenSpacer := rightWidth * 0.05 // 1%

	// 右侧区域
	inputSear := widget.NewEntry()
	inputSear.SetPlaceHolder("输入框 1")
	inputSear.SetText(appData.config.InputSear)

	inputLua := widget.NewEntry()
	inputLua.SetPlaceHolder("lua文件导出路径")
	inputLua.SetText(appData.config.InputLua)

	selectinputLua := widget.NewButton("浏览...", func() {
		dialog.NewFolderOpen(func(dir fyne.ListableURI, err error) {
			if err == nil && dir != nil {
				path := dir.String()[7:] // 移除 "file://" 前缀
				pathInput.SetText(path)
				appData.config.InputLua = path
			}
		}, myWindow).Show()
	})

	inputJson := widget.NewEntry()
	inputJson.SetPlaceHolder("json文件导出路径")
	inputJson.SetText(appData.config.InputJson)

	selectinputJson := widget.NewButton("浏览...", func() {
		dialog.NewFolderOpen(func(dir fyne.ListableURI, err error) {
			if err == nil && dir != nil {
				path := dir.String()[7:] // 移除 "file://" 前缀
				pathInput.SetText(path)
				appData.config.InputJson = path
			}
		}, myWindow).Show()
	})

	// 修改后的右侧布局
	rightPanel := container.NewVBox(
		widget.NewLabel("配置表路径"),
		container.NewBorder(nil, nil, nil, selectButton, pathInput),
		widget.NewLabel("lua导出文件保存路径"),
		container.NewBorder(nil, nil, nil, selectinputLua, inputLua),
		widget.NewLabel("json导出文件保存路径"),
		container.NewBorder(nil, nil, nil, selectinputJson, inputJson),
		createSpacer(0, inputSpacer), // 输入框间距 5%
		container.NewCenter(
			container.NewHBox(
				widget.NewButton("全部选中", func() { SelectAllFiles(appData.config.LastPath, appData.leftContent, appData, "", true) }),
				widget.NewButton("全部取消", func() { SelectAllFiles(appData.config.LastPath, appData.leftContent, appData, "", false) }),
				createSpacer(btnBetweenSpacer, 2), // 按钮间距 5%
				widget.NewButton("保存配置", func() {
					// 确保路径统一为系统格式
					appData.config.LastPath = filepath.Clean(appData.config.LastPath)
					pathInput.SetText(appData.config.LastPath) // 标准化显示

					appData.config.InputSear = inputSear.Text
					appData.config.InputLua = inputLua.Text
					appData.config.InputJson = inputJson.Text
					saveConfig(appData.config)
					fmt.Printf("%s ,", appData.config.LastPath)

					// 刷新文件列表（关键步骤）
					loadFiles(appData.config.LastPath, appData.leftContent, appData, appData.filtration,false)
				}),
				createSpacer(btnBetweenSpacer, 2), // 按钮间距 5%
				widget.NewButton("导出选中文件", func() { exportBtnCallBack(appData, myWindow) }),
			),
		),
	)

	split := container.NewHSplit(
		leftPanel,
		container.NewPadded(rightPanel),
	)

	//左右侧的初始化比例
	split.Offset = 0.45

	myWindow.SetContent(split)
	myWindow.ShowAndRun()
}

func SelectAllFiles(path string, container *fyne.Container, data *AppData, filtered string, isAll bool) {
	loadFiles(path, container, data, filtered, isAll)
}

// 使用正则表达式实现忽略大小写的模糊匹配
func matchIgnoreCaseRegex(pattern, text string) (bool, error) {
	regex, err := regexp.Compile("(?i)" + pattern) // (?i) 表示忽略大小写
	if err != nil {
		return false, err
	}
	return regex.MatchString(text), nil
}

func loadFiles(path string, container *fyne.Container, data *AppData, filtered string, isAll bool) {
	container.Objects = nil
	walkDir(path, func(filePath string) {
		if strings.HasSuffix(strings.ToLower(filePath), ".xlsx") {
			fileName := filepath.Base(filePath)
			matched, _ := matchIgnoreCaseRegex(filtered, fileName)
			if len(filtered) == 0 || matched { //strings.Contains(fileName, filtered)
				check := widget.NewCheck(fileName, func(checked bool) {
					data.selectedFiles[filePath] = checked
				})
				// 保留初始化选中状态
				data.selectedFiles[filePath] = isAll
				check.SetChecked(isAll)
				// if checked, exists := data.selectedFiles[filePath]; exists {
				// 	check.SetChecked(checked)
				// }
				container.Add(check)
			}

		}
	})
	container.Refresh()
}

func walkDir(root string, fn func(string)) {

	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			fn(path)
		}
		return nil
	})
	if err != nil {
		fmt.Println("目录遍历错误:", err)
	}
}

// 加载配置路径
func loadConfig() AppConfig {
	config := AppConfig{}
	if data, err := os.ReadFile(configFile); err == nil {
		json.Unmarshal(data, &config)
	}
	return config
}

// 保存配置路径
func saveConfig(config AppConfig) {
	if data, err := json.MarshalIndent(config, "", "  "); err == nil {
		os.WriteFile(configFile, data, 0644)
	}
}

// 获取选中文件列表
func getSelectedFiles(selectedFiles map[string]bool) []string {
	var files []string
	for path, selected := range selectedFiles {
		if selected {
			files = append(files, path)
		}
	}
	return files
}

// 处理文件的核心逻辑
func processFiles(app *AppData, files []string, progress *widget.ProgressBar, dia fyne.Window, mainWindow fyne.Window) {

	defer func() {
		fyne.Do(func() {
			// 清空搜索框并刷新
			// app.searchEntry.SetText("")
			// app.searchEntry.Refresh()

			mainWindow.Content().Refresh()
			loadFiles(app.config.LastPath, app.leftContent, app, app.filtration,false)
		})
	}()
	total := len(files)
	progress.Max = float64(total)

	var (
		wg         sync.WaitGroup
		successCnt atomic.Int32
		errorMsgs  []string
		errorLock  sync.Mutex
	)

	for i, file := range files {
		wg.Add(1)
		go func(index int, path string) {
			defer wg.Done()

			// 处理单个文件
			errors := processSingleFile(app, path)
			for _, err := range errors {
				if err != nil {
					errorLock.Lock()
					errorMsgs = append(errorMsgs, fmt.Sprintf("• %s: %v", filepath.Base(path), err))
					errorLock.Unlock()
				} else {
					successCnt.Add(1)
				}
			}
			// 更新进度（需要在主线程执行）
			fyne.Do(func() {
				progress.SetValue(float64(index + 1))
				mainWindow.Canvas().Refresh(progress)
			})
		}(i, file)
	}

	wg.Wait()
	//  显示完成提示
	successCount := len(files) - len(errorMsgs)
	errorCount := countErrors(errorMsgs)
	str := fmt.Sprintf("成功处理 %d 个文件/错误处理 %d 个文件", successCount, errorCount)
	fyne.Do(func() {
		dialog.ShowInformation("处理完成", str, mainWindow)
		// 如果需要显示详细错误
		if errorCount > 0 {
			errorContent := container.NewVScroll(
				container.NewVBox(
					widget.NewLabel("错误详情:"),
					widget.NewLabel(strings.Join(errorMsgs, "\n")),
				),
			)
			errorContent.SetMinSize(fyne.NewSize(400, 200))
			dialog.ShowCustom("错误列表", "关闭", errorContent, mainWindow)
		}
	})
	//去掉全部选择
	app.selectedFiles = map[string]bool{}

}

// 处理单个Excel文件
func processSingleFile(app *AppData, filePath string) []error {
	return Transform(app, filePath)
	// 可以在此添加实际业务逻辑，例如：
	// 1. 数据转换
	// 2. 生成新文件
	// 3. 数据汇总等
	// return nil
}

// 统计错误数量（需要根据实际错误记录实现）
func countErrors(errorMsgs []string) int {
	// 这里可以添加实际的错误统计逻辑
	return len(errorMsgs)
}

func exportBtnCallBack(app *AppData, win fyne.Window) {
	/* 导出逻辑 */
	// 获取选中的文件列表
	selected := getSelectedFiles(app.selectedFiles)
	if len(selected) == 0 {
		dialog.ShowInformation("提示", "请先选择要处理的文件", win)
		return
	}

	// 创建进度对话框
	progress := widget.NewProgressBar()

	// 在后台处理文件
	go processFiles(app, selected, progress, nil, win)

}
